using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace ZoomAndPan
{
    public partial class Canvas : UserControl
    {
        public Canvas()
        {
            this.SetStyle(ControlStyles.AllPaintingInWmPaint |
                ControlStyles.DoubleBuffer |
                ControlStyles.UserPaint |
                ControlStyles.ResizeRedraw,
                true);

            this.BackColor = Color.Gainsboro;
            this.BorderStyle = BorderStyle.FixedSingle;

            this.mIsMouseDown = false;
            this.mBlobs = new List<Point>();
            this.Zoom = 1.0f;
        }

     
        /// <summary>
        /// zoom factor of the canvas
        /// </summary>
        [
        Category("Appearance"),
        Description("The zoom factor. Less than 1 to reduce. More than 1 to magnify.")
        ]
        public float Zoom
        {
            get
            {
                return this.mZoom;
            }

            set
            {
                this.mZoom = Math.Min(4.0f, Math.Max(0.1f, value));
                this.RecalcAutosize();
            }
        }

        protected void RecalcAutosize()
        {
            this.AutoScrollMargin = new Size(10, 10);
            this.AutoScrollMinSize = new Size((int)(this.mZoom * 1024), (int)(this.mZoom * 768));
            //this.AutoScrollPosition = new Point(0, 0);

            this.Invalidate();
        }

        protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
        {
            base.OnMouseDown(e);

            this.mIsMouseDown = true;
            Point pt = this.BacktrackMouse(e);
            this.mBlobs.Add(pt);

            this.Invalidate();
        }

        protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if (this.mIsMouseDown)
            {
                Point pt = this.BacktrackMouse(e);
                this.mBlobs.Add(pt);
                this.Invalidate();
            }
        }

        protected override void OnMouseUp(System.Windows.Forms.MouseEventArgs e)
        {
            base.OnMouseUp(e);
            this.mIsMouseDown = false;
        }

        protected Matrix BuildTransformationMatrix()
        {
            //Creates the drawing matrix with the right zoom;
            Matrix mx = new Matrix(this.mZoom, 0, 0, this.mZoom, 0, 0);

            //pans it according to the scroll bars
            mx.Translate(this.AutoScrollPosition.X * (1.0f / this.mZoom), this.AutoScrollPosition.Y * (1.0f / this.mZoom));

            //center the width
            mx.Translate(Math.Max(0.0f, (this.ClientSize.Width / 2.0f - this.AutoScrollMinSize.Width / 2.0f) * (1.0f / this.mZoom)),
                Math.Max(0.0f, (this.ClientSize.Height / 2.0f - this.AutoScrollMinSize.Height / 2.0f) * (1.0f / this.mZoom)));

            return mx;
        }

        protected Point BacktrackMouse(MouseEventArgs e)
        {
            //Creates the drawing matrix with the right zoom;
            Matrix mx = this.BuildTransformationMatrix();

            //inverts it
            mx.Invert();

            //uses it to transform the current mouse position
            Point[] pa = new Point[] { new Point(e.X, e.Y) };
            mx.TransformPoints(pa);

            return pa[0];
        }

        protected override void OnScroll(ScrollEventArgs se)
        {
            Console.WriteLine("AutoScrollPosition = {0}", this.AutoScrollPosition);
            base.OnScroll(se);
        }

        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
        {
            //Creates the drawing matrix with the right zoom;
            Matrix mx = this.BuildTransformationMatrix();
            Rectangle bound = new Rectangle(0, 0, 1024, 768);
            SolidBrush sb = new SolidBrush(Color.Red);

            //use it for drawing
            e.Graphics.Transform = mx;


            e.Graphics.FillRectangle(Brushes.White, bound);
            e.Graphics.SetClip(bound);

            e.Graphics.SmoothingMode = SmoothingMode.HighQuality;

            //draw all the points
            foreach (Point pt in this.mBlobs)
                e.Graphics.FillEllipse(sb, pt.X - 5, pt.Y - 5, 10, 10);
            sb.Dispose();

            e.Graphics.ResetClip();
            e.Graphics.SmoothingMode = SmoothingMode.Default;
            if (this.AutoScrollMinSize.Width < this.ClientSize.Width)
            {
                e.Graphics.DrawLine(Pens.Black, bound.X, bound.Y, bound.X, bound.Height);
                e.Graphics.DrawLine(Pens.Black, bound.Width, bound.Y, bound.Width, bound.Height);
            }
            if (this.AutoScrollMinSize.Height < this.ClientSize.Height)
            {
                e.Graphics.DrawLine(Pens.Black, bound.X, bound.Y, bound.Width, bound.Y);
                e.Graphics.DrawLine(Pens.Black, bound.X, bound.Height, bound.Width, bound.Height);
            }
        }

        private float mZoom;
        private bool mIsMouseDown;
        private List<Point> mBlobs;
    }
}
